﻿using Spire.Pdf;
using Spire.Pdf.Graphics;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SuperKit
{
    class ImageProcessorPoolItem
    {
        public delegate void OnComplete(ImageProcessorPoolItem item);

        SpireConverter Parent { get; }
        public bool IsEnable { get; set; }
        public OnComplete Callback { get; private set; }
        public bool IsComplete { get; set; }
        private Image bitmap;
        private MemoryStream stream;
        public float MaxWidth => Parent.MaxWidth;
        public float MaxHeight => Parent.MaxHeight;
        public int Quality => Parent.Quality;
        public int Rotate => Parent.Rotate;
        public string StatusStr { get; private set; }
        /// <summary>用于识别的索引</summary>
        public int Index { get; private set; }
        /// <summary>要处理的文件名</summary>
        public string FileName { get; private set; }
        public bool IsStreamError { get; private set; }
        public bool IsLoadError { get; private set; }

        public SizeF CurrentSize;

        public ImageProcessorPoolItem(SpireConverter parent)
        {
            Parent = parent;
            IsEnable = true;
            IsComplete = false;
        }

        public void Use(string fileName, int index, OnComplete callback)
        {
            FileName = fileName;
            Index = index;
            IsEnable = false;
            IsComplete = false;
            Callback = callback;
        }

        public void Run()
        {
            Thread t = new Thread(Execute);
            t.Start();
        }

        public void Execute()
        {
            stream = new MemoryStream();
            try
            {
                IsLoadError = false;
                bitmap = Image.FromFile(FileName);
            }
            catch (Exception)
            {
                IsLoadError = true;
                bitmap = new Bitmap(100, 100);
            }

            // 检查是否需要旋转
            if (Rotate != 0)
            {
                if (Rotate == SpireConverter.ROTATE_VERTICAL)
                {
                    if (bitmap.Width > bitmap.Height)
                    {
                        bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
                    }
                }
                else if (Rotate == SpireConverter.ROTATE_HORIZONTAL)
                {
                    if (bitmap.Height > bitmap.Width)
                    {
                        bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
                    }
                }
            }

            CalculateSize();
            SaveToSteam();

            IsComplete = true;
            Callback?.Invoke(this);
        }

        private void CalculateSize()
        {
            // 都为0的时候，表示不对大小进行改变
            if (MaxWidth == 0 && MaxHeight == 0)
            {
                CurrentSize = new SizeF(bitmap.Width, bitmap.Height);
                return;
            }

            if (!(bitmap.Width > MaxWidth || bitmap.Height > MaxHeight))
            {
                CurrentSize.Width = bitmap.Width;
                CurrentSize.Height = bitmap.Height;
                return;
            }


            var rate = bitmap.Width / (float)bitmap.Height;
            if (bitmap.Width > bitmap.Height)
            {
                CurrentSize.Width = MaxWidth;
                CurrentSize.Height = MaxWidth / rate;
            }
            else
            {
                CurrentSize.Width = MaxHeight * rate;
                CurrentSize.Height = MaxHeight;
            }

            StatusStr += string.Format("RESIZE:{0}-{1} => {2}-{3}", bitmap.Width, bitmap.Height, CurrentSize.Width, CurrentSize.Height);

            ResizeBitmap();
        }


        private void ResizeBitmap()
        {
            Bitmap bmp = new Bitmap((int)CurrentSize.Width, (int)CurrentSize.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(bitmap, 0f, 0f, CurrentSize.Width, CurrentSize.Height);
            bitmap.Dispose();
            bitmap = bmp;
        }

        private void SaveToSteam()
        {
            IsStreamError = false;

            ImageCodecInfo jpgEncoder = Kit.GetEncoder(ImageFormat.Jpeg);
            System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
            EncoderParameters myEncoderParameters = new EncoderParameters(1);
            EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, Quality);
            myEncoderParameters.Param[0] = myEncoderParameter;

            try
            {
                bitmap.Save(stream, jpgEncoder, myEncoderParameters);
            }
            catch (ExternalException)
            {
                IsStreamError = true;
            }


        }


        public Stream GetStream()
        {
            return stream;
        }

        public Image GetImage()
        {
            return bitmap;
        }

        public void Release()
        {
            if (bitmap != null)
            {
                bitmap.Dispose();
                bitmap = null;
            }
            if (stream != null)
            {
                stream.Dispose();
                stream = null;
            }
            IsEnable = true;
            IsComplete = false;
        }
    }

    class SpireConverterPage
    {
        public const int READY = 0;
        public const int PROCESS = 1;
        public const int COMPLETE = 2;



        public SpireConverter Parent { get; }
        public PageSpliterPage Page { get; }
        public PageSpliter Pager => Parent.Pager;
        private delegate void FileCallback();

        public string Destinaton { get; private set; }
        public PdfDocument Doc { get; private set; }
        public int Total => Page.Count;
        public int Pointer { get; set; }
        public bool IsComplete { get; set; }
        public float MaxWidth => Parent.MaxWidth;
        public float MaxHeight => Parent.MaxHeight;
        public List<FileInfo> Files => Page.Files;
        public string CurrentFile { get; set; }
        public string StatusStr { get; set; }
        public int executeCount = 0;
        public int[] process;

        public SpireConverterPage(SpireConverter parent, PageSpliterPage page)
        {
            Parent = parent;
            Page = page;
            IsComplete = false;
        }

        private string CalculateOutput()
        {
            var output = Parent.TargetDir;
            if (Kit.IsEmpty(output))
            {
                return string.Format("{0}\\{1}{2}.pdf", Pager.Dir.Parent.FullName, Pager.Dir.Name, GetVolName());
            }
            else
            {
                return string.Format("{0}\\{1}{2}.pdf", output, Pager.Dir.Name, GetVolName());
            }
        }

        public void Execute()
        {
            Destinaton = CalculateOutput();
            Parent.Flush("输出文件{1}：{0}", Destinaton, Parent.UseCompress ? "(使用压缩)" : "");

            if (File.Exists(Destinaton))
            {
                var overrideStr = Parent.Arg.IsIgnoreExist ? "跳过" : "覆盖";
                Parent.Flush("输出文件已经存在({0})：{1}", overrideStr, Destinaton);
                if (Parent.Arg.IsIgnoreExist)
                {
                    IsComplete = true;
                    return;
                }
            }

            ExecuteDocument();
        }

        int completeCount = 0;
        private void ExecuteDocument()
        {
            if (Total == 0)
            {
                Parent.Log($"该位置没有获得任何有效图片:{Destinaton}");
                IsComplete = true;
                return;
            }

            Doc = new PdfDocument();
            Doc.CompressionLevel = PdfCompressionLevel.Best;
            Doc.PageLabels = new PdfPageLabels();


            Pointer = 0;
            executeCount = 0;
            process = new int[Total];
            var completeIndex = 0;

            for (int i = 0; i < process.Length; i++)
            {
                process[i] = READY;
            }

            while (true)
            {
                completeCount = 0;
                for (int i = 0; i < process.Length; i++)
                {
                    var stat = process[i];
                    if (stat == READY)
                    {
                        var item = GetAvailable();
                        if (item != null)
                        {
                            UseItem(item, i);
                        }
                    }
                    else if (stat == COMPLETE)
                    {
                        completeCount++;
                    }
                }

                if (process[completeIndex] == PROCESS)
                {
                    var completeItem = Parent.GetComplete(completeIndex);
                    if (completeItem != null)
                    {
                        OnComplete(completeItem);
                        completeIndex++;
                    }
                }




                if (completeIndex >= process.Length)
                {
                    break;
                }
            }

            OnEnding();
        }

        private void UseItem(ImageProcessorPoolItem item, int index)
        {
            process[index] = PROCESS;
            item.Use(GetName(index), index, null);
            item.Run();
        }

        private string GetName(int index)
        {
            return Page.Files[index].FullName;
        }

        private ImageProcessorPoolItem GetAvailable()
        {
            return Parent.GetAvailable();
        }

        private string GetVolName()
        {
            if (Pager.IsOnePage)
            {
                return Kit.EMPTY;
            }
            else
            {
                return string.Format(" Vol {0}", Page.Index + 1);
            }
        }

        private void OnEnding()
        {
            Doc.SaveToFile(Destinaton);
            Doc.Close();
            Parent.Log("完成({0}/{1})：{2}", completeCount, Total, Destinaton);
            IsComplete = true;
        }


        private void OnComplete(ImageProcessorPoolItem item)
        {
            Parent.SetStatus("[{0}/{1}]{2} 完成!", completeCount + 1, Total, Path.GetFileName(item.FileName));
            process[item.Index] = COMPLETE;

            if (item.IsStreamError || item.IsLoadError)
            {
                Parent.Flush("发生GDI错误，可能是该文件损坏：{0}", Path.GetFileName(item.FileName));
                item.Release();
                return;
            }

            var img = PdfImage.FromStream(item.GetStream());
            //var img = PdfImage.FromImage(item.GetImage());
            var page = Doc.Pages.Insert(item.Index, item.CurrentSize, new PdfMargins(0f));
            page.Canvas.DrawImage(img, new PointF(0f, 0f), item.CurrentSize);
            if (Parent.UseCompress)
            {
                page.TryCompressImage(0);
            }
            item.Release();

            GetPageLabelInfo(item.FileName, out string dir, out string fname, out int fcount, out int findex);
            Doc.PageLabels.AddRange(item.Index, PdfPageLabels.Decimal_Arabic_Numerals_Style, $"{findex}");


        }

        private static void GetPageLabelInfo(string fileName, out string dirName, out string fileNameWithoutExt, out int fileCount, out int fileIndex)
        {
            var dir = new DirectoryInfo(Path.GetDirectoryName(fileName));
            var files = dir.GetFiles();
            var count = files.Length;
            var index = -1;
            for (int i = 0; i < files.Length; i++)
            {
                if (Kit.IsEqual(files[i].FullName, fileName))
                {
                    index = i;
                    break;
                }
            }

            dirName = dir.Name;
            fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
            fileCount = count;
            fileIndex = index;
        }


    }



    class SpireConverter : CommandExecutor
    {
        public const int ROTATE_VERTICAL = 1;
        public const int ROTATE_HORIZONTAL = 2;

        public PageSpliter Pager { get; private set; }
        public SpireConverterPage[] Pages { get; private set; }
        public float MaxWidth { get; }
        public float MaxHeight { get; }
        public bool UseCompress { get; }
        public int PageCount { get; }
        public int RequireCount { get; }
        public int PoolCount { get; }
        public int Quality { get; }
        // 是否旋转，如果为0，不做任何处理。如果不为0，则将项目转为竖向（=1或=V），或是横向（=2或=H）。
        public int Rotate { get; }
        private List<ImageProcessorPoolItem> Items { get; set; }

        public SpireConverter(ArgReader arg, IExecutorOwner parent, string path) : base(arg, parent, path)
        {
            MaxWidth = arg.MaxWidth;
            MaxHeight = arg.MaxHeight;
            UseCompress = arg.IsZip;
            PageCount = arg.GetInteger("-pageCount");
            RequireCount = arg.GetInteger("-requireCount");
            PoolCount = arg.GetInteger("-thread", 1);
            Quality = arg.GetInteger("-quality", 80);

            Rotate = 0;
            string rotStr = arg.GetParameter("-rotate");
            if (!Kit.IsEmpty(rotStr))
            {
                if (Kit.IsEqual(rotStr, "1") || Kit.IsEqual(rotStr, "V"))
                {
                    Rotate = 1;
                }
                else if (Kit.IsEqual(rotStr, "2") || Kit.IsEqual(rotStr, "H"))
                {
                    Rotate = 2;
                }
            }

            Items = new List<ImageProcessorPoolItem>(PoolCount);
            for (int i = 0; i < PoolCount; i++)
            {
                Items.Add(new ImageProcessorPoolItem(this));
            }
        }

        public override void Execute()
        {
            base.Execute();

            DirectoryInfo dir = new DirectoryInfo(FilePath);
            Pager = new PageSpliter(FilePath, PageCount, RequireCount, IMG_EXTS);
            Pages = new SpireConverterPage[Pager.Page];

            Thread t = new Thread(StartPage);
            t.Start();
        }

        private void StartPage()
        {
            for (int i = 0; i < Pager.Page; i++)
            {
                Pages[i] = new SpireConverterPage(this, Pager.Pages[i]);
                Pages[i].Execute();
            }
            End();
        }

        public ImageProcessorPoolItem GetAvailable()
        {
            foreach (var i in Items)
            {
                if (i.IsEnable)
                {
                    return i;
                }
            }
            return null;
        }

        public ImageProcessorPoolItem GetComplete()
        {
            foreach (var i in Items)
            {
                if (i.IsComplete)
                {
                    return i;
                }
            }
            return null;
        }
        public ImageProcessorPoolItem GetComplete(int index)
        {
            foreach (var i in Items)
            {
                if (i.IsComplete && i.Index == index)
                {
                    return i;
                }
            }
            return null;
        }
    }
}
